package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	accessKey = "AKLTNjk2N2MyMGE1OGNiNGUzNzg0NzI1OTI2ZTg5NjViNGY"
	secretKey = "WkRNMFpUVTRNbU5tWTJNeE5ERTFZemsyTlRZeU9UazNNVEkxTkRBNE5EUQ=="
	host      = "translate.volcengineapi.com"
	region    = "cn-north-1"
	service   = "translate"
)

type TranslateRequest struct {
	SourceLanguage string   `json:"SourceLanguage"`
	TargetLanguage string   `json:"TargetLanguage"`
	TextList       []string `json:"TextList"`
	Category       string   `json:"Category,omitempty"`
}

type TranslateResponse struct {
	TranslationList []struct {
		Translation        string `json:"Translation"`
		DetectedSourceLang string `json:"DetectedSourceLang"`
	} `json:"TranslationList"`
	Error struct {
		Code    string `json:"Code"`
		Message string `json:"Message"`
	} `json:"Error"`
}

// splitText splits text into chunks of maxLength characters
func splitText(text string, maxLength int) []string {
	re := regexp.MustCompile(`([.!?])\s+`)
	sentences := re.Split(text, -1)
	var chunks []string
	currentChunk := strings.Builder{}

	for i, sentence := range sentences {
		if i < len(sentences)-1 {
			sentence += string(re.FindStringSubmatch(text)[1]) + " "
		}
		if currentChunk.Len()+len(sentence) <= maxLength {
			currentChunk.WriteString(sentence)
		} else {
			chunks = append(chunks, currentChunk.String())
			currentChunk.Reset()
			currentChunk.WriteString(sentence)
		}
	}
	if currentChunk.Len() > 0 {
		chunks = append(chunks, currentChunk.String())
	}
	return chunks
}

// signRequest generates the Volcengine API signature
func signRequest(method, path string, query url.Values, body []byte, timestamp string) http.Header {
	canonicalQuery := query.Encode()
	canonicalHeaders := "host:" + host + "\nx-date:" + timestamp + "\n"
	signedHeaders := "host;x-date"
	canonicalRequest := method + "\n" + path + "\n" + canonicalQuery + "\n" +
		canonicalHeaders + "\n" + signedHeaders + "\n" +
		fmt.Sprintf("%x", sha256.Sum256(body))

	algorithm := "HMAC-SHA256"
	date := timestamp[:8]
	credentialScope := date + "/" + region + "/" + service + "/request"
	stringToSign := algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" +
		fmt.Sprintf("%x", sha256.Sum256([]byte(canonicalRequest)))

	dateKey := hmacSHA256([]byte("AWS4"+secretKey), []byte(date))
	dateRegionKey := hmacSHA256(dateKey, []byte(region))
	dateRegionServiceKey := hmacSHA256(dateRegionKey, []byte(service))
	signingKey := hmacSHA256(dateRegionServiceKey, []byte("request"))
	signature := fmt.Sprintf("%x", hmacSHA256(signingKey, []byte(stringToSign)))

	credential := accessKey + "/" + credentialScope
	authHeader := algorithm + " Credential=" + credential + ", SignedHeaders=" + signedHeaders +
		", Signature=" + signature

	header := http.Header{}
	header.Set("Authorization", authHeader)
	header.Set("Content-Type", "application/json")
	header.Set("Host", host)
	header.Set("X-Date", timestamp)
	return header
}

func hmacSHA256(key []byte, data []byte) []byte {
	mac := hmac.New(sha256.New, key)
	mac.Write(data)
	return mac.Sum(nil)
}

func main() {
	// Initialize logging
	logFile, err := os.OpenFile("translate.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalf("Failed to open log file: %v", err)
	}
	defer logFile.Close()
	logger := log.New(io.MultiWriter(os.Stdout, logFile), "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)

	// Read article
	content, err := ioutil.ReadFile("article.txt")
	if err != nil {
		logger.Fatalf("Failed to read article.txt: %v", err)
	}
	if len(content) == 0 {
		logger.Fatal("article.txt is empty")
	}
	logger.Printf("Read %d bytes from article.txt: %s", len(content), string(content))

	// Pre-process: split text
	chunks := splitText(string(content), 500)
	if len(chunks) == 0 {
		logger.Fatal("No text chunks created")
	}
	logger.Printf("Split text into %d chunks", len(chunks))
	for i, chunk := range chunks {
		logger.Printf("Chunk %d: %s", i+1, chunk)
	}

	// Translate
	var translated []string
	client := &http.Client{Timeout: 10 * time.Second}

	for i := 0; i < len(chunks); i += 1000 {
		end := i + 1000
		if end > len(chunks) {
			end = len(chunks)
		}
		batch := chunks[i:end]
		logger.Printf("Processing batch %d with %d texts", i/1000+1, len(batch))

		// Prepare request
		reqBody := TranslateRequest{
			SourceLanguage: "en",
			TargetLanguage: "zh",
			TextList:       batch,
			Category:       "news",
		}
		body, err := json.Marshal(reqBody)
		if err != nil {
			logger.Printf("Failed to marshal request: %v", err)
			continue
		}
		logger.Printf("Request body: %s", string(body))

		// Retry logic
		var resp *http.Response
		var respBody []byte
		for retries := 0; retries < 3; retries++ {
			timestamp := time.Now().UTC().Format("20060102T150405Z")
			query := url.Values{
				"Action":  []string{"TranslateText"},
				"Version": []string{"2020-06-01"},
			}
			header := signRequest("POST", "/", query, body, timestamp)
			logger.Printf("Request headers: %+v", header)

			url := "https://" + host + "/?" + query.Encode()
			req, err := http.NewRequest("POST", url, bytes.NewReader(body))
			if err != nil {
				logger.Printf("Failed to create request: %v", err)
				continue
			}
			req.Header = header

			resp, err = client.Do(req)
			if err != nil {
				logger.Printf("Request failed (retry %d): %v", retries+1, err)
				time.Sleep(time.Second * time.Duration(retries+1))
				continue
			}

			respBody, err = ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				logger.Printf("Failed to read response body: %v", err)
				continue
			}

			if resp.StatusCode == 200 {
				break
			}
			logger.Printf("Non-200 response (retry %d): %d, %s", retries+1, resp.StatusCode, string(respBody))
			time.Sleep(time.Second * time.Duration(retries+1))
		}

		if resp == nil || resp.StatusCode != 200 {
			logger.Printf("Failed to get valid response for batch %d", i/1000+1)
			continue
		}

		// Parse response
		var translateResp TranslateResponse
		if err := json.Unmarshal(respBody, &translateResp); err != nil {
			logger.Printf("Failed to decode response: %v, body: %s", err, string(respBody))
			continue
		}
		logger.Printf("Response: %+v", translateResp)

		if translateResp.Error.Code != "" {
			logger.Printf("API error: %s - %s", translateResp.Error.Code, translateResp.Error.Message)
			continue
		}

		if len(translateResp.TranslationList) == 0 {
			logger.Printf("No translations returned for batch %d", i/1000+1)
			continue
		}

		for _, t := range translateResp.TranslationList {
			translated = append(translated, t.Translation)
			logger.Printf("Translated: %s", t.Translation)
		}

		logger.Printf("Translated %d texts in batch %d", len(translateResp.TranslationList), i/1000+1)

		// Save intermediate results
		if len(translated) > 0 {
			intermediateText := strings.Join(translated, "\n")
			if err := ioutil.WriteFile("intermediate.txt", []byte(intermediateText), 0644); err != nil {
				logger.Printf("Failed to write intermediate.txt: %v", err)
			} else {
				logger.Printf("Saved intermediate results to intermediate.txt")
			}
		}

		time.Sleep(100 * time.Millisecond)
	}

	// Check translations
	if len(translated) == 0 {
		logger.Fatal("No translations produced. Check logs for errors.")
	}

	// Post-process: merge and save
	translatedText := strings.Join(translated, "\n")
	logger.Printf("Merged %d translated chunks", len(translated))

	// Save final output
	err = ioutil.WriteFile("translated_article.txt", []byte(translatedText), 0644)
	if err != nil {
		logger.Fatalf("Failed to write translated_article.txt: %v", err)
	}
	logger.Printf("Translation completed. Output saved to translated_article.txt with %d characters", len(translatedText))
	fmt.Println("Translation completed. Check translated_article.txt")
}
